/*!  \file  FullSVD.h
 *	  \brief  Declares the FullSVD interface. In the corresponding cpp file all
 *	  tests for SVD implementation are included.
 * 
 *   \details   details
 *   \author    Yordan Madzhunkov German Research School for Simulation Sciences GmbH
 *   \version   1.0
 *   \date      04/13/2013 10:20:06 PM
 *   \copyright GNU Public License.
 */
#pragma once
#include "ImplementationFactory.h"
#include "UnitTester.h"

class Matrix;				// forward declare a class for a matrix
class BiReduction;		// reduction of full matrix to it's  Bi diagonal form
class BiSVD; 				// bidiagonal SVD
class FormSVD;				// computing the full SVD, from the bidiagonal one
class TriEigen; 			// tridiagonal symetric eigenvalue problem


class FullSVD : public Implementation {
public:
	FullSVD(const char * name_);
	FullSVD(BiReduction * red_, BiSVD * biSVD_, FormSVD * prod_);
	virtual ~FullSVD();
	void saveCompute(Matrix & A, Matrix & U, Matrix & Sigma, Matrix & VT); 
	bool acceptRowMajor() const { return aRowMajor; };
	bool acceptColMajor() const { return aColMajor; };
	virtual bool setNumberOfThreads(const int nThreads_);
protected:
	virtual void compute(Matrix & A, Matrix & U, Matrix & Sigma, Matrix & VT );
	char * label;				 	// the label of this specific implemetation 
	BiReduction 			* reduction;	// reduction of general to BiDiag
	BiSVD						* biSVD; 		// bidiagonal SVD
	FormSVD					* form; 			// computing full SVD, from BiDiag
	bool sanity, aColMajor, aRowMajor;
};


// Shortcut macros to define specific implementaions for each problem
#define FULLSVD(x) \
class x : public FullSVD { \
public: \
	x();\
	virtual ~x(){}; \
private: \
	void compute(Matrix & A, Matrix & U, Matrix & Sigma, Matrix & VT );\
	virtual bool setNumberOfThreads(const int nThreads_);\
};
#define DEFINESVD(x) \
class x : public FullSVD { \
public: \
	x();\
	virtual ~x(){}; \
};


UNITTEST2(FullSVDUnitTest, FullSVD, svd);

// BENCHMARKS
template <typename T> class Metric;
class MatrixGenerator;
class FullSVDBenchmark : public Benchmark {
public:
	FullSVDBenchmark(FullSVD & bisvd_);
	virtual ~FullSVDBenchmark();
	virtual void setRunConfiguration(const int n) ;
	virtual int  getProblemSize(const int configuration) const;
	virtual void prepareRun();
	virtual void makeRun();
	virtual void finalizeRun();
	enum { nSvdNorms = 2 };
protected:
	void addSVDNormsMetrics();
	void measureSVDNorms();
	DISALLOW_COPY_AND_ASSIGN(FullSVDBenchmark);
	MatrixGenerator  * generator;
	FullSVD          * svd;
	Matrix * A, * U, * V, *S, *A2;
	Metric<double >  * svdNormMetrics[2];
};

class FullSVDScaling : public Benchmark {
public:
	FullSVDScaling(FullSVD & bisvd_);
	virtual ~FullSVDScaling();
	virtual void setRunConfiguration(const int n) ;
	virtual int  getProblemSize(const int configuration) const;
	virtual void prepareRun();
	virtual void makeRun();
	virtual void finalizeRun();
	enum { nSvdNorms = 2 };
protected:
	void addSVDNormsMetrics();
	void measureSVDNorms();
	DISALLOW_COPY_AND_ASSIGN(FullSVDScaling);
	MatrixGenerator  * generator;
	FullSVD          * svd;
	Matrix * A, * U, * V, *S, *A2;
	Metric<double >  * svdNormMetrics[2];
};
